<template>
  <div class="max-w-4xl mx-auto p-6">
    <h1 class="text-2xl font-semibold">比较（Compare）</h1>
    <p class="mt-2 text-gray-600">比较两个文本的差异。支持按行 / 单词 / 字符 模式。</p>

    <div class="mt-6 space-y-4">
      <div class="flex items-center space-x-3">
        <label class="text-sm text-gray-700">模式：</label>
        <select v-model="mode" class="border rounded px-2 py-1">
          <option value="line">按行（Line）</option>
          <option value="word">按单词（Word）</option>
          <option value="char">按字符（Char）</option>
        </select>

        <label class="ml-4 flex items-center space-x-2 text-sm">
          <input type="checkbox" v-model="ignoreCase" class="form-checkbox" />
          <span>忽略大小写</span>
        </label>

        <div class="ml-auto text-sm text-gray-600">相似度: <span class="font-medium">{{ similarityPercent }}%</span></div>
      </div>

      <div class="flex space-x-4">
        <div class="flex-1">
          <div class="flex items-center justify-between mb-2">
            <label class="text-sm font-medium">文本 A</label>
            <div class="space-x-2">
              <button @click="() => fileARef.click()" class="text-sm text-blue-500">打开文件</button>
            </div>
          </div>
          <textarea v-model="textA" rows="12" class="w-full border rounded p-2" placeholder="在此粘贴或输入文本 A"></textarea>
          <input ref="fileARef" type="file" accept=".txt" class="hidden" @change="onFileAChange" />
        </div>

        <div class="flex-1">
          <div class="flex items-center justify-between mb-2">
            <label class="text-sm font-medium">文本 B</label>
            <div class="space-x-2">
              <button @click="() => fileBRef.click()" class="text-sm text-blue-500">打开文件</button>
            </div>
          </div>
          <textarea v-model="textB" rows="12" class="w-full border rounded p-2" placeholder="在此粘贴或输入文本 B"></textarea>
          <input ref="fileBRef" type="file" accept=".txt" class="hidden" @change="onFileBChange" />
        </div>
      </div>

      <div class="flex items-center space-x-3">
        <button @click="compareNow" class="primary-button">比较</button>
        <button @click="swap" class="secondary-button">交换</button>
        <button @click="clearAll" class="border px-3 py-2 rounded">清空</button>
        <button @click="copyRight" class="border px-3 py-2 rounded">复制 B</button>
        <button @click="downloadRight" class="border px-3 py-2 rounded">下载 B</button>
        <nuxt-link to="/text-tools" class="ml-auto text-sm text-blue-500">返回文本工具</nuxt-link>
      </div>

      <div class="pt-4 border-t">
        <div class="flex justify-between items-center mb-2">
          <div class="text-sm text-gray-600">比较结果（高亮显示新增 / 删除）</div>
          <div class="text-sm text-gray-500">A 移除: <span class="font-medium">{{ removedCount }}</span>  B 新增: <span class="font-medium">{{ addedCount }}</span></div>
        </div>

        <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
          <div>
            <div class="text-sm text-gray-700 mb-1">文本 A</div>
            <div class="p-3 border rounded bg-white text-sm whitespace-pre-wrap break-words" aria-live="polite">
              <template v-for="(seg, idx) in leftSegments" :key="idx">
                <span v-if="seg.type === 'equal'">{{ seg.text }}</span>
                <span v-else-if="seg.type === 'delete'" class="bg-red-100 text-red-800 px-1 rounded">{{ seg.text }}</span>
                <span v-else class="text-gray-400">{{ seg.placeholder }}</span>
              </template>
            </div>
          </div>

          <div>
            <div class="text-sm text-gray-700 mb-1">文本 B</div>
            <div class="p-3 border rounded bg-white text-sm whitespace-pre-wrap break-words" aria-live="polite">
              <template v-for="(seg, idx) in rightSegments" :key="idx">
                <span v-if="seg.type === 'equal'">{{ seg.text }}</span>
                <span v-else-if="seg.type === 'insert'" class="bg-green-100 text-green-800 px-1 rounded">{{ seg.text }}</span>
                <span v-else class="text-gray-400">{{ seg.placeholder }}</span>
              </template>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, watch, onMounted } from 'vue'
import { useTools } from '~/composables/useTools'

const { copyToClipboard, downloadFile, readFile, debounce } = useTools()

const textA = ref('')
const textB = ref('')
const mode = ref('line') // 'line' | 'word' | 'char'
const ignoreCase = ref(true)

const fileARef = ref(null)
const fileBRef = ref(null)

const leftSegments = ref([])
const rightSegments = ref([])

const addedCount = ref(0)
const removedCount = ref(0)
const equalCount = ref(0)

const separatorForMode = (m) => (m === 'line' ? '\n' : m === 'word' ? ' ' : '')

const tokenize = (text, m) => {
  if (!text) return []
  if (m === 'line') return text.split(/\r?\n/)
  if (m === 'word') return text.split(/\s+/).filter(Boolean)
  return text.split('')
}

// LCS-based diff (returns ops referencing indices so we can display original tokens)
const computeOps = (a, b) => {
  const n = a.length
  const m = b.length
  const dp = Array.from({ length: n + 1 }, () => Array(m + 1).fill(0))
  for (let i = n - 1; i >= 0; i--) {
    for (let j = m - 1; j >= 0; j--) {
      if (a[i] === b[j]) dp[i][j] = dp[i + 1][j + 1] + 1
      else dp[i][j] = Math.max(dp[i + 1][j], dp[i][j + 1])
    }
  }

  const ops = []
  let i = 0, j = 0
  while (i < n && j < m) {
    if (a[i] === b[j]) {
      ops.push({ type: 'equal', ai: i, bj: j })
      i++; j++
    } else if (dp[i + 1][j] >= dp[i][j + 1]) {
      ops.push({ type: 'delete', ai: i }); i++
    } else {
      ops.push({ type: 'insert', bj: j }); j++
    }
  }
  while (i < n) { ops.push({ type: 'delete', ai: i }); i++ }
  while (j < m) { ops.push({ type: 'insert', bj: j }); j++ }
  return ops
}

const buildSegments = () => {
  const aTokensRaw = tokenize(textA.value, mode.value)
  const bTokensRaw = tokenize(textB.value, mode.value)

  const aTokens = ignoreCase.value ? aTokensRaw.map(t => t.toLowerCase()) : aTokensRaw.slice()
  const bTokens = ignoreCase.value ? bTokensRaw.map(t => t.toLowerCase()) : bTokensRaw.slice()

  const ops = computeOps(aTokens, bTokens)

  const sep = separatorForMode(mode.value)

  const left = []
  const right = []
  let adds = 0, dels = 0, eqs = 0

  for (const op of ops) {
    if (op.type === 'equal') {
      const rawA = aTokensRaw[op.ai] ?? ''
      const display = rawA + sep
      left.push({ type: 'equal', text: display })
      right.push({ type: 'equal', text: display })
      eqs++
    } else if (op.type === 'delete') {
      const rawA = aTokensRaw[op.ai] ?? ''
      const display = rawA + sep
      left.push({ type: 'delete', text: display })
      right.push({ type: 'placeholder', placeholder: sep })
      dels++
    } else if (op.type === 'insert') {
      const rawB = bTokensRaw[op.bj] ?? ''
      const display = rawB + sep
      left.push({ type: 'placeholder', placeholder: sep })
      right.push({ type: 'insert', text: display })
      adds++
    }
  }

  leftSegments.value = left
  rightSegments.value = right
  addedCount.value = adds
  removedCount.value = dels
  equalCount.value = eqs
}

const compareNow = () => {
  try {
    buildSegments()
  } catch (err) {
    console.error('比较失败', err)
  }
}

const debouncedCompare = debounce(compareNow, 200)

watch([textA, textB, mode, ignoreCase], () => {
  debouncedCompare()
})

const similarityPercent = computed(() => {
  const max = Math.max(tokenize(textA.value, mode.value).length, tokenize(textB.value, mode.value).length)
  if (max === 0) return 100
  return Math.round((equalCount.value / max) * 100)
})

const swap = () => { const t = textA.value; textA.value = textB.value; textB.value = t; }
const clearAll = () => { textA.value = ''; textB.value = ''; leftSegments.value = []; rightSegments.value = []; addedCount.value = removedCount.value = equalCount.value = 0 }

const copyRight = async () => {
  const ok = await copyToClipboard(textB.value)
  if (ok) alert('已复制到剪贴板')
  else alert('复制失败')
}

const downloadRight = () => {
  downloadFile(textB.value || '', 'compare-b.txt', 'text/plain')
}

const onFileAChange = async (e) => {
  const f = e.target.files && e.target.files[0]
  if (!f) return
  try {
    const content = await readFile(f)
    textA.value = content
  } catch (err) {
    console.error('读取文件失败', err)
    alert('读取文件失败')
  } finally {
    e.target.value = ''
  }
}

const onFileBChange = async (e) => {
  const f = e.target.files && e.target.files[0]
  if (!f) return
  try {
    const content = await readFile(f)
    textB.value = content
  } catch (err) {
    console.error('读取文件失败', err)
    alert('读取文件失败')
  } finally {
    e.target.value = ''
  }
}

onMounted(() => {
  compareNow()
})
</script>
